SyncCatch is a debugging tool that catches synchronous File Manager and Device Manager requests made from code running as the result of an interrupt. If it catches one of these illegal synchronous requests, it drops into MacsBug with a message.
Why are synchronous File Manager and Device Manager requests made at interrupt time illegal?
Synchronous File Manager and Device Manager requests made at interrupt time can deadlock or crash the system. See the develop magazine article “Asynchronous Routines on the Macintosh” in issue 13 if you’d like more detailed information.
SyncCatch catches requests made at interrupt time with system patches that. The patches check for four conditions to determine if the request was made at interrupt time:
1. The 68K interrupt level. If the interrupt level is not zero, then interrupts are disabled and synchronous requests should be made.
2. VBL tasks are executing. The Mac OS executes VBL tasks at interrupt level zero. However, VBL tasks execute as a result of an interrupt so synchronous requests are still illegal.
3. Deferred Task Manager tasks are executing. The Mac OS executes Deferred Task Manager tasks at interrupt level zero. However, Deferred Task Manager tasks execute at interrupt time so synchronous requests are still illegal.
4. Secondary Interrupt Handlers are executing. The Mac OS executes Secondary Interrupt Handlers at interrupt level zero. However, Secondary Interrupt Handlers execute at interrupt time so synchronous requests are still illegal.
What should I do when SyncCatch drops into MacsBug with a message?
If it happened to me, I’d want to find out what code was making the illegal request. That way, I could tell the publisher of that code to fix the problem, or if it were my code, I could fix it before someone caught me crashing the system.
Here’s some hints you can use to find the faulty code:
1. First look at SyncCatch’s message. It’ll tell you if the illegal request was a File Manager or Device Manager request. It’ll also tell you why the synchronous request shouldn’t have been made (interrupts disabled, executing VBL tasks, executing Deferred Tasks, or executing Secondary Interrupt Handlers).
2. Look at the parameter block passed to the request using MacsBug’s DM command and the appropriate memory template. I usually start with the IOParamBlockRec template because it's pretty generic.
You type this:
DM RA0 IOParamBlockRec
MacsBug shows you something like this:
Displaying IOParamBlockRec at 000003A4
000003A4 qLink NIL
000003A8 qType 0002 = ioQType
000003AA ioTrap A005 _Status
000003AC ioCmdAddr 001AD512 ->
000003B0 ioCompletion NIL
000003B4 ioResult FFEE
000003B6 ioNamePtr NIL
000003BA ioVRefNum 0008 = Guts
000003BC ioRefNum FFCA = .ATADisk
000003BE ioVersNum #0
000003BF ioPermssn #43
000003C0 ioMisc 666C7573 is a bad pointer
000003C4 ioBuffer 00214A20 ->
000003C8 ioReqCount 00001000
000003CC ioActCount 00001000
000003D0 ioPosMode 0001 = fsFromStart
000003D2 ioPosOffset 05883000
000003D6 ioWPosOffset.lo 5E8CC000
MacsBug will show you the parameter block used to make the request. SyncCatch sets up the ioTrap field the same way the File and Device Managers do, so MacsBug will show you what request was made by name.
3. Stack crawl using MacsBug’s SC7 command to find out what code called the File Manager or Device Manager.
If you’re lucky, it’ll be in code you wrote but haven’t shipped. If not, then you’ll have to look further to figure out what code made the illegal request.
Note: If register A0 contains $03A4 (as shown in the example above), then the driver request is being made in response to a File Manager request. You can find out what File Manager request was made by looking at the request at the head of the File Manager request queue ($0362). You type this:
DM 362^ IOParamBlockRec
MacsBug shows you something like this:
Displaying IOParamBlockRec at 074249EA
074249EA qLink NIL
074249EE qType 0005 = fsQType
074249F0 ioTrap 0209 = PBGetCatInfoSync
074249F2 ioCmdAddr 001AEE5A ->
074249F6 ioCompletion NIL
074249FA ioResult 0001
074249FC ioNamePtr 0744AA72 -> "SyncCatch.tdm"
07424A00 ioVRefNum FFFF = Guts
07424A02 ioRefNum 0000
07424A04 ioVersNum #0
07424A05 ioPermssn #0
07424A06 ioMisc 00030200 ->
07424A0A ioBuffer 54444154 is a bad pointer
07424A0E ioReqCount 43574945
07424A12 ioActCount 01000000
07424A16 ioPosMode 0000 = fsAtMark
07424A18 ioPosOffset 00000001
07424A1C ioWPosOffset.lo 245B0000
In this case, the File Manager request that caused the synchronous device Read was a synchronous PBGetCatInfo request.
Can I leave SyncCatch installed all of the time? How much does it slow my system down?
You can leave SyncCatch installed all of the time -- it shouldn’t affect performance much and from my testing with Mac OS 7.6, it looks like there is nothing in the Mac OS system code making illegal synchronous requests. Worst case, the patch code adds around sixty 68K instructions to every File Manager request and to _Read, _Write, _Control, and _Status requests made to device drivers.
I installed SyncCatch and during the icon parade, its icon is X'd out. How come?
You probably don’t have MacsBug installed. SyncCatch won’t install if there’s no low-level debugger present.
Is there anything you know of that sets off SyncCatch?
Yes, using the “Force Quit” key sequence (command-option-escape) or using MacsBug’s ES command at interrupt time will set off SyncCatch because they both call ExitToShell. ExitToShell closes any files opened by the application and closes the application file. The _Close calls to the File Manager are synchronous and since they’re made at interrupt time, SyncCatch will catch them. In general, force quitting an application should only be done if the application has hung or crashed, and then, you should probably restart your system as soon as possible because ExitToShell performs all sorts of things that should really only be done at non-interrupt time.
Is there an easy way to set break points with MacsBug on code that shouldn't be called at interrupt time?
Yes, get the latest MacsBug (version 6.6 or later) and use the built in TaskLevel expression. TaskLevel will resolve to zero if it is not interrupt time. For example, if you found that PBGetCatInfoSync was being called at interrupt time and wanted to catch the code making the PBGetCatInfoSync call, you could set both an ATrap break and a TVector Break to catch that by typing this in MacsBug:
APIB PBGetCatInfoSync TaskLevel!=0
(APIB is a macro that sets both the ATrap and TVector break for a specified routine.)
The bits in TaskLevel are defined in the public interface Debugging.h. TaskLevel is available for use within programs starting with Mac OS 9.0.
Revision History
1.0 First release
2.0 Added checks for secondary interrupt handler execution time.
2.1 Fixed check for secondary interrupt handler (SyncCatch would sometimes give false positives and say secondary interrupt handlers were running when they weren't). Updated the Read Me to include more debugging tips.